package com.zkjl.protect_business.service.impl;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.IdUtil;
import com.zkjl.protect_business.common.BaseException;
import com.zkjl.protect_business.dto.PageBaseDTO;
import com.zkjl.protect_business.dto.SortDTO;
import com.zkjl.protect_business.dto.TemplateDTO;
import com.zkjl.protect_business.entity.*;
import com.zkjl.protect_business.repository.BusinessRepository;
import com.zkjl.protect_business.repository.PBFileRepository;
import com.zkjl.protect_business.repository.TemplateRepository;
import com.zkjl.protect_business.service.BusinessService;
import com.zkjl.protect_business.service.FileService;
import com.zkjl.protect_business.service.LogService;
import com.zkjl.protect_business.service.TemplateService;
import com.zkjl.protect_business.utils.ExcelUtil;
import com.zkjl.protect_business.utils.QueryUtil;
import com.zkjl.protect_business.utils.WordUtil;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.index.query.*;
import org.modelmapper.ModelMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

import static com.zkjl.protect_business.common.Consts.*;
import static com.zkjl.protect_business.common.Status.DATA_EXIST;
import static com.zkjl.protect_business.common.Status.DATA_NOT_EXIST;

@Service
public class TemplateServiceImpl implements TemplateService {
    @Autowired
    TemplateRepository templateRepository;
    @Autowired
    BusinessRepository businessRepository;
    @Autowired
    FileService fileService;
    @Autowired
    PBFileRepository fileRepository;

    @Autowired
    BusinessService businessService;

    @Autowired
    ElasticsearchRestTemplate elasticsearchRestTemplate;
    @Autowired
    ModelMapper modelMapper;

    @Autowired
    LogService logService;

    @Override
    public Template upload(MultipartFile file, String businessId) throws IOException {


        Business business = businessService.get(businessId);
        String name = business.getName();
        List<BusinessField> fields = business.getFields();

        //解析模板
        Template template = parsing(file, fields,business);

        template.setBusiness(name);
        template.setBusinessId(businessId);

        String fileId = fileService.uploadFile(file);

        if (StringUtils.isNotBlank(fileId)) {
            template.setFileId(fileId);

            //保存到文件信息里面
            PBFile pbFile = fileService.get(fileId);
            pbFile.setFields(template.getFields());
            pbFile.setType(template.getType());
            fileRepository.save(pbFile);
        }

        logService.save("模板", template.getTemplateName(), OPERATION_UPLOAD);

        return template;
    }



    @Override
    public Template update(String id, TemplateDTO templateDTO) {

        Template template = get(id);
        template.setBusinessId(templateDTO.getBusinessId());
        template.setDescribe(template.getDescribe());

        Template save = templateRepository.save(template);
        logService.save("模板", template.getTemplateName(), OPERATION_EDIT);

        return save;
    }

    @Override
    public Template save(TemplateDTO templateDTO) {

        Optional<Business> optionalbusiness = businessRepository.findById(templateDTO.getBusinessId());
        Business business = optionalbusiness.get();
        String name = business.getName();

        Template template = modelMapper.map(templateDTO, Template.class);
        template.setId(IdUtil.simpleUUID());
        template.setBusiness(name);
        template.setBaseInfo();

        String fileId = templateDTO.getFileId();
        PBFile pbFile = fileService.get(fileId);
        String type = pbFile.getType();
        List<DocField> fields = pbFile.getFields();
        template.setFields(fields);
        template.setType(type);
        template.setTemplateName(pbFile.getName());

        Template save = templateRepository.save(template);
        logService.save("模板", template.getTemplateName(), OPERATION_ADD);
        return save;
    }

    @Override
    public List<Template> list(String businessId) {

        List<Template> templateList = templateRepository.findAllBybusinessId(businessId);

        return templateList;
    }

    @Override
    public PageImpl page(PageBaseDTO pageBaseDTO, SortDTO sortDTO, String type, String businessId) {

        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        BoolQueryBuilder qb = QueryBuilders.boolQuery();

        if (StringUtils.isNotEmpty(type)) {
            qb.filter(new QueryStringQueryBuilder(type).field("type").defaultOperator(Operator.AND));
        }

        if (StringUtils.isNotEmpty(businessId)) {
            qb.filter(new QueryStringQueryBuilder(businessId).field("business_id").defaultOperator(Operator.AND));
        }

        Pageable pageable = QueryUtil.build(qb, queryBuilder, pageBaseDTO, sortDTO, Template.class);

        queryBuilder.withQuery(qb);

        SearchHits<Template> search1 = elasticsearchRestTemplate.search(queryBuilder.build(), Template.class);
        List<Template> collect = search1.stream().map(SearchHit::getContent).collect(Collectors.toList());

        long total = search1.getTotalHits();

        PageImpl page = new PageImpl(collect, pageable, total);

        return page;

    }

    @Override
    public Template get(String templateId) {

        Optional<Template> repository = templateRepository.findById(templateId);
        if (!repository.isPresent()) {
            throw new BaseException(DATA_NOT_EXIST.getCode(), "未找到模板信息");
        }
        Template template = repository.get();

        return template;
    }

    @Override
    public Boolean del(String id) {
        Template template = get(id);
        String templateName = template.getTemplateName();

        //删除模板下的文件
        String fileId = template.getFileId();
        fileRepository.deleteById(fileId);

        logService.save("模板", templateName, OPERATION_DEL);
        templateRepository.deleteById(id);
        return true;
    }


    /**
     * 解析字段
     *
     * @param file
     * @param fields 所属业务已有的字段
     * @return
     * @throws IOException
     */
    public Template parsing(MultipartFile file, List<BusinessField> fields, Business business) throws IOException {


        String orgFieldName = business.getOrgFieldName();
        if (StringUtils.isEmpty(orgFieldName)) {
            throw new BaseException(DATA_NOT_EXIST.getCode(), "请先指定业务中的单位字段！");
        }

        String originalFilename = file.getOriginalFilename();
        String suffix = FileUtil.getSuffix(originalFilename);

        Template template = new Template();
        String id = IdUtil.simpleUUID();
        template.setId(id);
        template.setBaseInfo();
        template.setTemplateName(originalFilename);
        List<DocField> docFields = new ArrayList<>();
        if ("doc,docx".contains(suffix.toLowerCase())) {
            template.setType("word");
            docFields = WordUtil.parsingField(file);

        } else if ("xls,xlsx".contains(suffix.toLowerCase())) {
            template.setType("excel");
            docFields = ExcelUtil.parsingField(file);
        }

        //所有字段类型映射
        Map<String, Object> mapping = new HashMap<>();
        fields.stream().forEach(field -> {
            String key = field.getKey();
            String type = field.getType();
            mapping.put(key, type);
        });

        //是否有单位字段
        AtomicReference<Boolean> havaOrg = new AtomicReference<>(false);

        //判定是否有重复的key
        List<String> keys = new ArrayList<>();
        docFields.stream().forEach(docField -> {
            String key = docField.getKey();

            if (key.equals(orgFieldName)) {
                havaOrg.set(true);
            }

            if (StringUtils.isBlank(key)) {
                throw new BaseException(DATA_NOT_EXIST.getCode(), "字段不能为空，请确认后重试！");
            }
            if (keys.contains(key)) {
                throw new BaseException(DATA_EXIST.getCode(), "字段 " + key + "已存在，请确认修改之后重新上传！");
            }
            String type = MapUtil.getStr(mapping, key);
            docField.setType(type);
            keys.add(key);
        });

        if (!havaOrg.get()) {
            throw new BaseException(DATA_NOT_EXIST.getCode(), "必须包含" + orgFieldName + "字段");
        }

        template.setFields(docFields);

        return template;

    }

}
